package com.linkhub.oms.component.transactional.aspect;

import com.linkhub.oms.common.annotation.AfterCommit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.concurrent.Executor;

/**
 * @Description: AfterTransactionCommitAspect
 * @Author: echo
 * @Date: 2024/3/9 21:59
 * @Version: 1.0
 */
@Slf4j
@Aspect
public class AfterTransactionCommitAspect {

    private static final ThreadLocal<Boolean> IN_AFTER_COMMIT = new ThreadLocal<>();

    private Executor afterCommitExecutor;

    public AfterTransactionCommitAspect(Executor afterCommitExecutor){
        this.afterCommitExecutor = afterCommitExecutor;
    }

    /**
     * 切面
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Around("@annotation(com.linkhub.oms.common.annotation.AfterCommit)")
    public void afterCommit(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("AfterTransactionCommitAspect afterCommit start >>>");

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AfterCommit annotation = signature.getMethod().getAnnotation(AfterCommit.class);

        if (annotation.async()) {
            // 异步
            asyncExecute(joinPoint);
        } else {
            // 同步
            syncExecute(joinPoint);
        }
    }

    /**
     * 同步执行
     * 1、已进入 afterCommit 或 未在事务中  则直接执行
     * 2、否则注册事务处理后执行
     *
     * @param joinPoint
     * @throws Throwable
     */
    private void syncExecute(ProceedingJoinPoint joinPoint) throws Throwable {
        /*
         * 同步的情况下，需要判断是否已经注册过了
         */
        if (Boolean.TRUE.equals(IN_AFTER_COMMIT.get())
                || !TransactionSynchronizationManager.isActualTransactionActive()) {
            log.info("AfterTransactionCommitAspect sync - has been registered, execute direct");
            joinPoint.proceed();
            return;
        }

        // 标识已进入
        IN_AFTER_COMMIT.set(Boolean.TRUE);
        // 注册调用器
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                //具体的操作
                try {
                    log.info("AfterTransactionCommitAspect sync - execute logic start");
                    joinPoint.proceed();
                    log.info("AfterTransactionCommitAspect sync - execute logic end");
                } catch (Throwable throwable) {
                    log.error("syncExecute error:", throwable);
                }
            }

            @Override
            public void afterCompletion(int status) {
                // 移除标识
                IN_AFTER_COMMIT.remove();
            }
        });
    }

    /**
     * 异步执行
     *
     * @param joinPoint
     */
    private void asyncExecute(ProceedingJoinPoint joinPoint) {
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            /*
             * 同步的情况下，多次注册会失败
             */
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    //具体的操作
                    asyncProceed(joinPoint);
                }
            });
            return;
        }
        asyncProceed(joinPoint);
    }

    private void asyncProceed(ProceedingJoinPoint joinPoint) {
        afterCommitExecutor.execute(() -> {
            try {
                log.info("AfterTransactionCommitAspect async - execute logic start");
                joinPoint.proceed();
                log.info("AfterTransactionCommitAspect async - execute logic end");
            } catch (Throwable throwable) {
                log.error("AfterTransactionCommitAspect afterCommit error:", throwable);
            }
        });
    }

}
